package com.pb.service.boot.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.apache.ibatis.session.SqlSessionFactory;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

/**
 * 主数据源自定义配置类
 * @author liudebao
 * @version v1.0.0
 */
@Configuration
@PropertySource("classpath:config-prod.properties")
// 扫描 Mapper 接口并容器管理
@MapperScan(basePackages = MasterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDataSourceConfig {
    /**包扫描配置项*/
    // 精确到 master 目录，以便跟其他数据源隔离
    static final String PACKAGE = "com.pb.**.mapper.**";
    /**xml扫描配置项*/
    static final String MAPPER_LOCATION = "classpath*:mapping/*/**Mapper.xml";
    static final String MAPPER_LOCATION_OTH = "classpath*:mapping/*/*/**Mapper.xml";
    static final String MAPPER_LOCATION_IND = "classpath*:mapping/**Mapper.xml";
    static final String MAPPER_LOCATION_PB = "classpath*:mapping/*/*/*/**Mapper.xml";
    /**url*/
    @Value("${jdbc.url}")
    private String url;
    /**用户名*/
    @Value("${jdbc.username}")
    private String user;
    /**密码*/
    @Value("${jdbc.password}")
    private String password;
    /**驱动程序类*/
    @Value("${jdbc.driverClassName}")
    private String driverClass;


    @Value("${master.datasource.MaxActive}")
    private int maxActive;

    @Value("${master.datasource.MaxWait}")
    private int maxWait;

    /*@Value("${master.datasource.TimeBetweenConnectErrorMillis}")
    private int timeBetweenConnectErrorMillis;*/

    @Value("${master.datasource.MinEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;

    @Value("${master.datasource.TestWhileIdle}")
    private boolean testWhileIdle;

    @Value("${master.datasource.TestOnBorrow}")
    private boolean testOnBorrow;

    @Value("${master.datasource.TestOnReturn}")
    private boolean testOnReturn;

    @Value("${master.datasource.PoolPreparedStatements}")
    private boolean poolPreparedStatements;

    @Value("${master.datasource.MaxPoolPreparedStatementPerConnectionSize}")
    private int maxPoolPreparedStatementPerConnectionSize;

    @Value("${master.datasource.TimeBetweenEvictionRunsMillis}")
    private int timeBetweenEvictionRunsMillis;

    @Value("${master.datasource.InitialSize}")
    private int initialSize;

    @Value("${master.datasource.MinIdle}")
    private int minIdle;

    @Value("${jasypt.encryptor.password}")
    private String encryptorPWD;

    /**
     * 主数据源
     * @return
     */
    @Bean(name = "masterDataSource")
    @Primary
    public DataSource masterDataSource() {
        StandardPBEStringEncryptor encryptor =new StandardPBEStringEncryptor();
        encryptor.setPassword(encryptorPWD);
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(encryptor.decrypt(user));
        dataSource.setPassword(encryptor.decrypt(password));

        dataSource.setInitialSize(initialSize);
        dataSource.setMinIdle(minIdle);

        dataSource.setMaxActive(maxActive);
        dataSource.setMaxWait(maxWait);

        dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        //dataSource.setTimeBetweenConnectErrorMillis(timeBetweenConnectErrorMillis);
        dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        dataSource.setTestWhileIdle(testWhileIdle);
        dataSource.setTestOnBorrow(testOnBorrow);
        dataSource.setTestOnReturn(testOnReturn);
        dataSource.setPoolPreparedStatements(poolPreparedStatements);
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        if (driverClass.contains("oracle")) {
            dataSource.setValidationQuery("select 1 from dual");
        }else {
            dataSource.setValidationQuery("select 1");
        }
        try {
            dataSource.setFilters("stat, wall");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return dataSource;
    }

    /**
     * 主事务管理
     * @return
     */
    @Bean(name = "masterTransactionManager")
    @Primary
    public DataSourceTransactionManager masterTransactionManager() {
        return new DataSourceTransactionManager(masterDataSource());
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(masterDataSource());
    }

    @Bean
    public DatabaseIdProvider databaseIdProvider() {
        DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider(){
            @Override
            public String getDatabaseId(DataSource dataSource) {
                String databaseId = super.getDatabaseId(dataSource);
                if (StringUtils.isBlank(databaseId)) {
                    throw new NullPointerException("数据库标识等于空，请检查数据库连接情况！");
                }
                return databaseId;
            }
        };
        Properties p = new Properties();
        p.setProperty("Oracle", "oracle");
        p.setProperty("MySQL", "mysql");
        databaseIdProvider.setProperties(p);
        return databaseIdProvider;
    }

    /**
     * 主sql session工厂
     * @param masterDataSource
     * @return
     * @throws Exception
     */
    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(masterDataSource);
        sessionFactory.setMapperLocations(resolveMapperLocations());
        sessionFactory.setDatabaseIdProvider(databaseIdProvider());
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setCallSettersOnNulls(true);
        configuration.setDatabaseId("oracle");
        sessionFactory.setConfiguration(configuration);
        //sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
        //sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(resolveMapperLocations()));
        return sessionFactory.getObject();
    }

    public Resource[] resolveMapperLocations() {
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        List<String> mapperLocations = new ArrayList<>();
        mapperLocations.add(MasterDataSourceConfig.MAPPER_LOCATION);
        mapperLocations.add(MasterDataSourceConfig.MAPPER_LOCATION_OTH);
        mapperLocations.add(MasterDataSourceConfig.MAPPER_LOCATION_IND);
        mapperLocations.add(MasterDataSourceConfig.MAPPER_LOCATION_PB);
        List<Resource> resources = new ArrayList();
        if (mapperLocations != null) {
            for (String mapperLocation : mapperLocations) {
                try {
                    Resource[] mappers = resourceResolver.getResources(mapperLocation);
                    resources.addAll(Arrays.asList(mappers));
                } catch (IOException e) {
                    // ignore
                }
            }
        }
        return resources.toArray(new Resource[resources.size()]);
    }
}
